package com.jfinal.ext.generator;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import com.jfinal.kit.StrKit;

/**
 * Model 生成器
 */
public class ModelGenerator {
    protected String packageTemplate = "package %s.%s;%n%n";
    protected String importTemplate = "import com.jfinal.plugin.activerecord.Db;%n"
            + "import com.jfinal.plugin.activerecord.Page;%n%n" + "import %s.%s.%s;%n%n";
    protected String classDefineTemplate = "/**%n" + " * Model, Generated by Robot on %s.%n" + " */%n"
            + "@SuppressWarnings(\"serial\")%n" + "public class %s extends %s<%s> {%n";
    protected String daoTemplate = "\tpublic static final %s dao = new %s();%n%n";

    protected String pageSampleTemplate = "%n\tpublic Page<%s> page(int pageNumber, int pageSize) {%n"
            + "\t\treturn %s.dao.paginate(pageNumber, pageSize, \"select *\", \"from %s order by %s asc\");%n"
            + "\t}%n%n";

    protected String pageClauseTemplate = "\tpublic Page<%s> page(int pageNumber, int pageSize, String orderField, String orderDirection, String where) {%n"
            + "\t\treturn %s.dao.paginate(pageNumber, pageSize, \"select * \", String.format(\"from %s where 1=1 %s order by %s %s\", where, orderField, orderDirection));%n"
            + "\t}%n%n";

    protected String batchDeleteTemplate = "\tpublic int batchDelete(String ids) {%n"
            + "\t\treturn Db.update(String.format(\"delete from %s where %s in (%s) \", ids));%n" + "\t}%n%n";

    protected String saveOrUpdateTemplate = "\tpublic boolean saveOrUpdate(%s model, boolean isSave) {\n"
            + "\t\tboolean flag = false;\n" + "\t\tif (model.get%s() == null) {\n"
            + "\t\t\t//model.setCreateTime(new Date());\n" + "\t\t\tflag = model.save();\n" + "\t\t}\n" + "\t\telse {\n"
            + "\t\t\t//model.setEditTime(new Date());\n" + "\t\t\tflag = model.update();\n" + "\t\t}\n"
            + "\t\treturn flag;\n" + "\t}%n%n";

    protected String modelPackageName;
    protected String baseModelPackageName;
    protected String modelOutputDir;
    protected boolean generateDaoInModel = true;

    public ModelGenerator(String modelPackageName, String baseModelPackageName, String modelOutputDir) {
        if (StrKit.isBlank(modelPackageName))
            throw new IllegalArgumentException("modelPackageName can not be blank.");
        if (modelPackageName.contains("/") || modelPackageName.contains("\\"))
            throw new IllegalArgumentException("modelPackageName error : " + modelPackageName);
        if (StrKit.isBlank(baseModelPackageName))
            throw new IllegalArgumentException("baseModelPackageName can not be blank.");
        if (baseModelPackageName.contains("/") || baseModelPackageName.contains("\\"))
            throw new IllegalArgumentException("baseModelPackageName error : " + baseModelPackageName);
        if (StrKit.isBlank(modelOutputDir))
            throw new IllegalArgumentException("modelOutputDir can not be blank.");

        this.modelPackageName = modelPackageName;
        this.baseModelPackageName = baseModelPackageName;
        this.modelOutputDir = modelOutputDir;
    }

    public void setGenerateDaoInModel(boolean generateDaoInModel) {
        this.generateDaoInModel = generateDaoInModel;
    }

    public void generate(List<TableMeta> tableMetas) {
        System.out.println("Generate model ...");
        for (TableMeta tableMeta : tableMetas)
            genModelContent(tableMeta);
        wirtToFile(tableMetas);
    }

    protected void genModelContent(TableMeta tableMeta) {
        StringBuilder ret = new StringBuilder();
        genPackage(tableMeta, ret);
        genImport(tableMeta, ret);
        genClassDefine(tableMeta, ret);
        genDao(tableMeta, ret);
        genPageSampleMethod(tableMeta, ret);
        genPageClauseMethod(tableMeta, ret);
        genBatchDeleteMethod(tableMeta, ret);
        genSaveOrUpdateMethod(tableMeta, ret);
        ret.append(String.format("}%n"));
        tableMeta.modelContent = ret.toString();
    }

    protected void genPackage(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(packageTemplate, modelPackageName, "model"));
    }

    protected void genImport(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(importTemplate, baseModelPackageName, "model.base", tableMeta.baseModelName));
    }

    protected void genClassDefine(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(classDefineTemplate, Constants.DATE_TIME, tableMeta.modelName, tableMeta.baseModelName,
                tableMeta.modelName));
    }

    protected void genDao(TableMeta tableMeta, StringBuilder ret) {
        if (generateDaoInModel)
            ret.append(String.format(daoTemplate, tableMeta.modelName, tableMeta.modelName));
        else
            ret.append(String.format("\t%n"));
    }

    protected void genPageSampleMethod(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(pageSampleTemplate, tableMeta.modelName, tableMeta.modelName, tableMeta.name,
                tableMeta.primaryKey));
    }

    protected void genPageClauseMethod(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(pageClauseTemplate, tableMeta.modelName, tableMeta.modelName, tableMeta.name,
                Constants.PLACEHOLDER, Constants.PLACEHOLDER, Constants.PLACEHOLDER));
    }

    protected void genBatchDeleteMethod(TableMeta tableMeta, StringBuilder ret) {
        ret.append(String.format(batchDeleteTemplate, tableMeta.name, tableMeta.primaryKey, "%s"));
    }

    protected void genSaveOrUpdateMethod(TableMeta tableMeta, StringBuilder ret) {
        String getterMethodName = StrKit.firstCharToUpperCase(StrKit.toCamelCase(tableMeta.primaryKey));
        ret.append(String.format(saveOrUpdateTemplate, tableMeta.modelName, getterMethodName));
    }

    protected void wirtToFile(List<TableMeta> tableMetas) {
        try {
            for (TableMeta tableMeta : tableMetas)
                wirtToFile(tableMeta);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 若 model 文件存在，则不生成，以免覆盖用户手写的代码
     */
    protected void wirtToFile(TableMeta tableMeta) throws IOException {
        File dir = new File(modelOutputDir + File.separator + "model" + File.separator);
        if (!dir.exists())
            dir.mkdirs();

        String target = modelOutputDir + File.separator + "model" + File.separator + File.separator
                + tableMeta.modelName + ".java";

        File file = new File(target);
        if (file.exists()) {
            return; // 若 Model 存在，不覆盖
        }

        FileWriter fw = new FileWriter(file);
        try {
            fw.write(tableMeta.modelContent);
        } finally {
            fw.close();
        }
    }
}
